using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Transactions;
using Hermes.Framework.FileSystem;

namespace Hermes.Framework.Implementation
{
    public class SimpleResourceManager : IResourceManager
    {
        protected Stack<ITransactableOperation> undoLog;

        protected Queue<ITransactableOperation> redoLog;

        protected INonTransactableFileSystem fileSystem = null;

        protected Transaction tx;

        public SimpleResourceManager()
        {
            this.undoLog = new Stack<ITransactableOperation>();
            this.redoLog = new Queue<ITransactableOperation>();
        }

        public INonTransactableFileSystem FileSystem
        {
            set
            {
                this.fileSystem = value;
            }
            get
            {
                return this.fileSystem;
            }
        }

        public void WriteRedoLog(ITransactableOperation operation)
        {
            this.redoLog.Enqueue(operation);
        }

        public void WriteUndoLog(ITransactableOperation operation)
        {
            this.undoLog.Push(operation);
        }

        /// <summary>
        /// Start to participate into an external transaction.
        /// </summary>
        public void Enlist()
        {
            this.tx = Transaction.Current;
            if (this.tx == null)
            {
                //throw new ApplicationException("There is currently no transaction running");
                // do something...
            }
            else
            {
                this.redoLog.Clear();
                this.undoLog.Clear();

                this.tx.EnlistVolatile(this, EnlistmentOptions.None);
            }
        }

        public bool IsEnlisted()
        {
            return this.tx != null;
        }

        #region IEnlistmentNotification
        void IEnlistmentNotification.InDoubt(Enlistment enlistment)
        {
            // does nothing
        }

        void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
        {
            preparingEnlistment.Prepared();
        }

        void IEnlistmentNotification.Commit(Enlistment enlistment)
        {
            ITransactableOperation operation = null;
            while (this.redoLog.Count > 0)
            {
                operation = redoLog.Dequeue();
                operation.Commit();
                Trace.WriteLine(string.Format("Commit: {0}", operation.GetType().Name), "Transaction");
            }
            enlistment.Done();
            Console.WriteLine("Committed");
        }

        void IEnlistmentNotification.Rollback(Enlistment enlistment)
        {
            ITransactableOperation operation = null;
            while (this.undoLog.Count > 0)
            {
                operation = this.undoLog.Pop();
                operation.Rollback();
                Trace.WriteLine(string.Format("Rollback: {0}", operation.GetType().Name), "Transaction");
            }
            enlistment.Done();
            Console.WriteLine("Rollbacked");
        }
        #endregion

        public void Dispose()
        {
            this.tx = null;
        }
    }
}
